import java.util.Vector;

public class Board {
    ///for internal use in move calculation
    static final int[][] sTryDelta=
    {
        {-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}
    };
    
    static final int cSize=8;                ///< size of board

    ///these constants are used to specify the content of a cell of the board
    ///they are also used when we want to say whose turn it is to move, or which player mad a move
    static final byte EMPTY=0;               ///< the cell is empty
    static final byte OWN=1;                 ///< the cell belongs to us (the one playing)
    static final byte OTHER=2;               ///< the cell belongs to the other player
    
    ///initializes the board to the starting position
 
    ///pFirst indicates if we are going to play first
    Board(boolean pFirst)
    {
        mCell=new byte[cSize*cSize];

        for(int i=0;i<cSize;i++)
        {
            for(int j=0;j<cSize;j++)
            {
                if(i>=cSize/2-1&&i<=cSize/2&&j>=cSize/2-1&&j<=cSize/2)
                {
                    if((i==j&&!pFirst)||(i!=j&&pFirst))
                        mCell[i*cSize+j]=OWN;
                    else
                        mCell[i*cSize+j]=OTHER;
                }
                else
                    mCell[i*cSize+j]=EMPTY;
            }
            mOwn=2;
            mOther=2;
        }            
    }
    
    
    ///constructs a board which is the result of applying move \p pMove to board \p pRH
    
    /// \param pBoard the starting board position
    /// \param pMove the movement to perform
    ///
    /// \sa DoMove()
    Board(Board pBoard,Move pMove)
    {
        mCell=pBoard.mCell.clone();
        mOwn=pBoard.mOwn;
        mOther=pBoard.mOther;
        DoMove(pMove);
    }
    
    ///number of own pieces
    int OwnCount()          {        return mOwn;        }
    ///number of opponent's pieces
    int OtherCount()        {        return mOther;        }

    ///returns the content of a cell in the board.
    byte At(int pPos)
    {
        return mCell[pPos];
    }
    
    ///returns the content of a cell in the board.
    byte At(int pR,int pC)
    {
        return mCell[pR*cSize+pC];
    }

    ///private version of above function (allows modifying cells)
    private void Set(int pR,int pC,byte pValue)
    {
            mCell[pR*cSize+pC]=pValue;
    }

    /// returns a list of all valid moves for \p pWho
    
    /// \param pMoves a vector where the list of moves will be appended
    /// \param pWho the \ref ECell code (OWN or OTHER) of the
    /// player making the move
    void FindPossibleMoves(Vector<Move> pMoves,byte pWho)
    {
        pMoves.clear();
    
        assert(pWho==OWN||pWho==OTHER);
        byte lOther=(byte)(pWho^(OWN|OTHER));

        for(int r=0;r<cSize;r++)
        {
            for(int c=0;c<cSize;c++)
            {
                if(At(r,c)!=EMPTY) continue;
                
                boolean lFound=false;
                
                for(int t=0;t<8;t++)
                {
                    for(int nr=r+sTryDelta[t][0],nc=c+sTryDelta[t][1];
                        nr<cSize&&nc<cSize&&nr>=0&&nc>=0;
                        nr+=sTryDelta[t][0],nc+=sTryDelta[t][1])
                    {
                        if(At(nr,nc)==EMPTY) break;
                        if(At(nr,nc)==lOther) continue;
                        if(nr!=r+sTryDelta[t][0]||nc!=c+sTryDelta[t][1])
                            lFound=true;
                        break;
                    }
                }
                
                if(lFound)
                {
                    pMoves.add(new Move(r,c,pWho));
                }
            }
        }
    }
    
    ///transforms the board by performing a move

    ///it doesn't check that the move is valid, so you should only use
    ///it with moves returned by FindPossibleMoves 
    
    /// \param pMove the move to perform
    void DoMove(Move pMove)
    {
        if(pMove.IsPass()) return;
        
        int r=pMove.Row(),c=pMove.Col();
        int lWho=pMove.Who();
        int lOther=lWho^(OWN|OTHER);
        
        int lTurned=0;
        
        for(int t=0;t<8;t++)
        {
            boolean lFound=false;
            
            for(int nr=r+sTryDelta[t][0],nc=c+sTryDelta[t][1];
                nr<cSize&&nc<cSize&&nr>=0&&nc>=0;
                nr+=sTryDelta[t][0],nc+=sTryDelta[t][1])
            {
                if(At(nr,nc)==EMPTY) break;
                if(At(nr,nc)==lOther) continue;
                if(nr!=r+sTryDelta[t][0]||nc!=c+sTryDelta[t][1])
                    lFound=true;
                break;
            }
            
            if(lFound)
            {
                for(int nr=r+sTryDelta[t][0],nc=c+sTryDelta[t][1];
                    nr<cSize&&nc<cSize&&nr>=0&&nc>=0;
                    nr+=sTryDelta[t][0],nc+=sTryDelta[t][1])
                {
                    if(At(nr,nc)==lOther)
                    {
                        lTurned++;
                        Set(nr,nc,(byte)lWho);
                    }
                    else
                        break;
                }
            }
        }
        
        Set(r,c,(byte)lWho);
        
        if(lWho==OWN)
        {
            mOwn+=lTurned+1;
            mOther-=lTurned;
        }
        else
        {
            mOther+=lTurned+1;
            mOwn-=lTurned;
        }
    }

    ///prints the board
    
    ///Useful for debug purposes. Don't call it in the final version.
    void Print()
    {
        for(int r=0;r<cSize;r++)
        {
            for(int c=0;c<cSize;c++)
            {
                if((At(r,c)&OWN)!=0)
                {
                    System.out.print((char)27+"[1;37;46m()"+(char)27+"[0m");
                }
                else if((At(r,c)&OTHER)!=0)
                {
                    System.out.print((char)27+"[30;46m()"+(char)27+"[0m");
                }
                else
                {
                    System.out.print((char)27+"[1;37;46m  "+(char)27+"[0m");
                }
            }
            System.out.print("\n");
        }
        System.out.print("\n");
    }

    ///prints the board (no color version)
    
    ///Useful for debug purposes. Don't call it in the final version.
    void PrintNoColor()
    {
        for(int r=0;r<cSize;r++)
        {
            for(int c=0;c<cSize;c++)
            {
                if((At(r,c)&OWN)!=0)
                {
                    System.out.print("OO");
                }
                else if((At(r,c)&OTHER)!=0)
                {
                    System.out.print("XX");
                }
                else
                {
                    System.out.print("  ");
                }
            }
            System.out.print("\n");
        }
        System.out.print("\n");
    }

    private byte[] mCell;
    private int mOwn,mOther;
}
